﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WordNet.Core.DataContract;
using WordNet.Core.ElementContracts;
using WordNet.Core.Extensions;

namespace WordNet.Core
{
    [Export(typeof(IWNDictionary))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class WNDictionary: IWNDictionary
    {
        IDataContext _dataContext;
        
        public WNDictionary()
        {
           _dataContext = WNDictionary.Container.GetExportedValue<IDataContext>();        
        }

        public WNDictionary(IDataContext dc)
        {
            _dataContext = dc;
        }
       
        public IIndexWord FindWord(string lemma, POS pos)
        {
            // try different technics
            HashSet<string> possible = new HashSet<string>();
            possible.Add(lemma);
            possible.Add(lemma.ToWhitespace());
            possible.Add(lemma.UnderscoreToHyphen());
            possible.Add(lemma.HyphenToUnderscore());
            possible.Add(lemma.StripPeriods());
            possible.Add(lemma.StripUderscore());
            possible.Add(lemma.StripHyphen());

            IIndexWord result = null;
            foreach (var w in possible)
            {
                 result = GetIndexWord(w, pos);
                 if (result != null)
                     break;
            }
            return result;
        }
        
        public IIndexWord GetIndexWord(string lemma, POS pos)
        {
            return GetIndexWord(new IndexWordID(lemma, pos));
        }

        public IIndexWord GetIndexWord(IIndexWordID id)
        {
            return _dataContext.GetItem<IIndexWord>(id);
        }

        public IEnumerable<IIndexWord> GetAllIndexWords(POS pos)
        {
            return _dataContext.GetAll<IIndexWord>(pos);
        }
        
        public Dictionary<POS, IIndexWord> GetAllIndexWordsOf(string lemma)
        {
            Dictionary<POS, IIndexWord> indexWords = new Dictionary<POS, IIndexWord>();
            foreach (var item in POS.Values())
            {
                IIndexWord word = GetIndexWord(lemma, item);
                if (word != null)
                    indexWords.Add(item, word);
            }
            return indexWords;
        }
       
        public IWord GetWord(IWordID id)
        {
            ISynset synset = GetSynset(id.SynsetID);
            if (synset == null)
                return null;

            if (id.WordNumber > 0)
            {
                return synset.GetWords()[id.WordNumber - 1];
            }
            else if (id.Lemma != null)
            {
                return synset.GetWords().FirstOrDefault(s => s.Lemma.Equals(id.Lemma, StringComparison.OrdinalIgnoreCase));
            }
            else
            {
                throw new ArgumentException(); 
            }
        }

        public IWord GetWord(ISenseKey key)
        {
            ISenseEntry se = GetSenseEntry(key);
            ISynset syn = GetSynset(new SynsetID(se.Offset, se.PartOfSpeech));
            if (syn != null)
            {
                return syn.GetWords().SingleOrDefault(s => s.SenseKey.Equals(key));
            }

            // alternative approache
            IIndexWord iw = GetIndexWord(key.Lemma, key.PartOfSpeech);
            if (iw != null)
            {
                foreach (IWordID wordId in iw.GetWordIDs())
                {
                    var possibleWord = GetWord(wordId);
                    if (possibleWord != null)
                    {
                        var result = possibleWord.Synset.GetWords().SingleOrDefault(s => s.Lemma.Equals(key.Lemma));
                        if (result != null)
                            return result;
                    }
                }
            }
            return null;
        }

        public ISynset GetSynset(ISynsetID id)
        {
            return _dataContext.GetItem<ISynset>(id);
        }

        public IEnumerable<ISynset> GetAllSynsets(POS pos)
        {
            return _dataContext.GetAll<ISynset>(pos);
        }
        
        public Dictionary<POS, List<ISynset>> GetAllSynsetsOf(string lemma)
        {
            Dictionary<POS, List<ISynset>> synsets = new Dictionary<POS, List<ISynset>>();
            var indexWords = GetAllIndexWordsOf(lemma);
            foreach (var item in indexWords)
            {
                synsets.Add(item.Key, new List<ISynset>());
                foreach (var id in item.Value.GetWordIDs())
                {
                    synsets[item.Key].Add(GetSynset(id.SynsetID));
                }
            }

            return synsets;
        }
        
        public ISenseEntry GetSenseEntry(ISenseKey key)
        {
            return _dataContext.GetItem<ISenseEntry>(key);
        }

        public IEnumerable<ISenseEntry> GetAllSenseEntries()
        {
            return _dataContext.GetAll<ISenseEntry>(null);
        }

        public IExceptionEntry GetExceptionEntry(string surfaceForm, POS pos)
        {
            return GetExceptionEntry(new ExceptionEntryID(surfaceForm, pos));
        }

        public IExceptionEntry GetExceptionEntry(IExceptionEntryID id)
        {
            return _dataContext.GetItem<IExceptionEntry>(id);
        }

        public IEnumerable<IExceptionEntry> GetAllExceptionEntries(POS pos)
        {
            return _dataContext.GetAll<IExceptionEntry>(pos);
        }

        public IWordDefinition GetWordDefinition(string lemma, POS pos)
        {
            IIndexWord indexWord = GetIndexWord(new IndexWordID(lemma, pos));
            Dictionary<ISynsetID, ISynset> synsets = new Dictionary<ISynsetID, ISynset>();
            Dictionary<IWordID, IWord> words = new Dictionary<IWordID, IWord>();
            foreach (var wordId in indexWord.GetWordIDs())
            {
                synsets.Add(wordId.SynsetID, GetSynset(wordId.SynsetID));
                words.Add(wordId, GetWord(wordId));

            }
            return new WordDefinition(indexWord, synsets, words);
        }
       
        public IDataProvider<T> GetDataProvider<T>()
        {
            return _dataContext.GetDataProvider<T>();
        }
        
        public static CompositionContainer Container { get; set; }
    }
}
